-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update explainer for the new spec #137
Conversation
This updates the explainer text according to the new spec we agreed in the 09-15-2020 CG meeting. Some of the text is taken from [the first version](https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions-v1.md) of the proposal. I renamed `catch_br` to `delegate` based on the perceived consensus in WebAssembly#133. There are some minor issues that need clarification, some of which is currently being discussed in the repo: - `rethrow`'s immmediate (or label) argument. I made it match the status in the first proposal for now. I believe keeping it makes it more versatile and lets us handle more languages. - It is not clear whether `rethrow` and `delegate`'s label (=immediate) argument should count non-try block-like structures, such as blocks and loops. It is currently being discussed for `delegate`. - `delegate`'s opcode should be decided. We exhausted all control-flow opcodes that begin with `0x0`.
proposals/Exceptions.md
Outdated
|
||
The `unwind` block is meant to contain cleanup instructions, such as | ||
destructors, in case any instruction in the corresponding try block throws. | ||
Currently the `unwind` instruction has the same semantics as the `catch_all` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm surprised and confused with these semantics of unwind
.
If try ... unwind ... end
is semantically equivalent to try ... catch_all ... rethrow end
, then I can't see why we are adding this instruction. Can't we just postpone this try ... unwind ... end
to a future two-phase unwinding proposal? In fact, if the unwind
semantics will change later, couldn't that create backwards compatibility problems?
Until this document, I had understood unwind
more as a primitive related to common-lisp's unwind-protect
, which btw seems to be related to Java's and Python's finally
instead of catch_all
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also it seems that unwind
could spark a very long discussion, for example when common-lisp was being specified, unwind-protect
related discussions spanned over years (see this link, at the bottom of the page).
If we can avoid adding this instruction now, we could have these discussions in a different proposal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ioannad It's good to point out that there are good reasons to be wary of adding control constructs. Fortunately, though, in the case of unwind
, the control construct has been tried out in a wide variety of languages (with finally
being a close relative) and its semantics are fairly well understood. It seems to have been found to be particularly useful in multi-language settings for reasons akin to why it was added here to make modules forwards compatible with two-phase exception handling. And nowadays its semantics are fairly well understood, at least at the level that WebAssembly operates at. (This wasn't the case back when Common Lisp was being specified, and some of the complications in the referenced discussion have to do with higher-level considerations.)
I cannot say the same for catch_all
or rethrow
though, so rather than removing unwind
due to the above equivalence, my suggestion would be to remove catch_all
instead (if you were to remove anything at all).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ioannad They have the same semantics, and they are planned to have different semantics in two-phase. In single-phase unwinding, both catch_all
's and unwind
's bodies run as we search up and unwind the stack. In two-phase, catch
bodies run after we finish all two-phase unwinding process, while unwind
bodies run as cleanups in the second phase, and their end
instruction act as "resuming the second phase" instruction.
I think what you said also makes sense that if two are equivalent, why adding them now? I don't have a strong opinion either way myself. It was taken from @RossTate's suggestion, and I agreed with that for the reason that it'd be good if we make the current proposal and the follow-on future two-phase proposal as similar as possible, so that toolchains targeting EH proposals don't need to change where to put their cleanup code when they switch to the next proposal. (But there will be some other changes necessary for toolchains.)
@RossTate As I said in the other issue, catch_all
has its uses. If we remove that, unwind
cannot replace it when two-phase arrives. These days, I am almost getting an impression that you are trying to remove every single instruction that you don't need yourself for your future proposals. (By the way, I don't want this PR to be another discussion for removal of instructions.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
though, in the case of
unwind
, the control construct has been tried out in a wide variety of languages
@RossTate, can you point us to any such language? Constructs like finally
or unwind-protect
seem much more common-place and semantically well-understood.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I understand, you would have to make significant changes to LLVM to get that to happen, including changing how it represents CFGs and exception handling. My take is that finally
is a surface-level construct, and that unwind
is part of its lower-level counterpart, with code duplication or #124 providing ways to support the rest of its lower-level counterpart.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the LLVM backend gets by without any use of finally/unwind right now, so, shrug? And of course it is trivial to express fault a.k.a. unwind with such a flagged finally if needs be.
I'd still be interested in knowing which .NET producers actually use fault.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the LLVM backend gets by without any use of finally/unwind right now, so, shrug?
Every function call in LLVM has an argument specifying how to unwind it.
I'd still be interested in knowing which .NET producers actually use fault.
It's how .NET addresses the problem above that C++ destructors have different semantics on the two kinds of exit paths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About LLVM: It compiles destructors for the normal path and the exception path separately, because the exception path contains some different logic, such as, if another exception arises within there, it does not throw but calls std::terminate
or something. This is C++, but it also compiles finally
away for some languages that have finally
, such as Objective-C.
In the CFG level, LLVM has an invoke
instruction, which is a variant of call
instruction. invoke
takes two successors: a normal destination, where we should go when the call returns normally, and unwind destination, where we should unwind to when the call throws. CFG's control flow logic is very direct and simple; all are just go-to edges (or unwind edges) and they are directly encoded. Our wasm backend lowers this to try
-catch
.
So my point is, if we introduce finally
as a replacement of unwind
, at least for LLVM it is not usable for us. And I am not 100% sure if I understood your fragmented stack argument, but I think finally
has all the same problem anyway, no?
We can still use catch
for cleanup code as we have so far, but for 2PEH we need to distinguish user catch handler from cleanup code. That doesn't have to be unwind
, but what I'm saying is we need a way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I can see that it's not useful with the specific choices LLVM made. But other compilers could benefit. If there is pressure to add finally, then we shouldn't end up having both unwind and finally, thus the idea of a simple generalisation that expresses both.
And I am not 100% sure if I understood your fragmented stack argument, but I think finally has all the same problem anyway, no?
That is unrelated. That problem problem occurs with the search phase, not the unwind phase. The latter runs on the top of the stack, since all other stack frames are already unwound at that point.
proposals/Exceptions.md
Outdated
|
||
The current plan is, try-unwind blocks will have a different semantics from | ||
that of try-catch blocks when we introduce two-phase unwinding as a follow-on | ||
proposal in the future. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would really help if this plan could be sketched in this explainer somehow (or some separate doc to point to). Because to be honest, I still don't understand it, nor how the current semantics of unwind
could be changed/generalised later in a backwards-compatible manner.
proposals/Exceptions.md
Outdated
|
||
The `unwind` block is meant to contain cleanup instructions, such as | ||
destructors, in case any instruction in the corresponding try block throws. | ||
Currently the `unwind` instruction has the same semantics as the `catch_all` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
though, in the case of
unwind
, the control construct has been tried out in a wide variety of languages
@RossTate, can you point us to any such language? Constructs like finally
or unwind-protect
seem much more common-place and semantically well-understood.
This is an attempt to formally describe @aheejin's 3rd proposal, which she presented to the Wasm CG, and which was voted to be the new EH proposal, on September 18, 2020. This is not formal spec that I have developed, but a formal description of this 3rd proposal. This is a reworked form of my [first attempt on this formal spec overview](WebAssembly#87 (comment)) edited with my new understanding of the spec based on the discussion below, and in other issues, and according to @aheejin 's [3rd proposal overview](https://github.com/WebAssembly/exception-handling/blob/f7a4f60d11fb6326fc13f84d3889b11d3873f08a/proposals/Exceptions.md) in PR WebAssembly#137. This is in the form of a document as @tlively [requested](WebAssembly#142 (comment)), to make discussion on specific points easier. I wrote this formal spec overview roughly in the style of the 2nd proposal's [formal spec overview](WebAssembly#87 (comment)) by @rossberg. Particular points of interest: - In this I assume `rethrow` does have an immediate, as it is now described in WebAssembly#137. - The instruction `unwind` now reduces to `catch_all ... rethrow` as specified in Heejin's overview. - Because unwind is much simpler in this MVP, there is no `throw_point` anymore and `caught_m` is much simpler. - The introduction of `caught_m` now also introduces a label around the catch instructions, which label a rethrow instruction can reference. - Added explanation of the peculiar side condition in try's execution rule - just trying to make the rules more compact. I would be happy if anyone could point out things that are wrong or that I misunderstood.
Aheejin, I just want to ask a few questions:
Many Thanks |
Can't guarantee an exact timeline, but we hope we can get it work with LLVM/Clang/Emscripten at least within Q1 2021.
I think they will stay mostly the same. At least so far I don't plat to change them. |
Previously we assumed `rethrow`'s argument was always 0, but it turned out `rethrow` follows the same rule with `br` or `delegate`: WebAssembly/exception-handling#137 WebAssembly/exception-handling#146 (comment) Currently `rethrow`s generated by our backend always rethrow the exception caught by the innermost enclosing catch, so this adds a function to compute that and replaces `rethrow`'s argument with its computed result. This also renames `EHPadStack` in `InstPrinter` to `TryStack`, because in CFGStackify we use `EHPadStack` to mean the range between `catch`~`end`, while in `InstPrinter` we used it to mean the range between `try`~`catch`, so choosing different names would look clearer. Doesn't contain any functional changes in `InstPrinter`. Reviewed By: dschuff Differential Revision: https://reviews.llvm.org/D96595
I read through the new explainer text and it looks good to me, I agree with merging it and sorting out remaining issues such as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have just a few comments, but otherwise LGTM!
A try-catch block contains zero or more `catch` blocks and zero or one | ||
`catch_all` block. All `catch` blocks should precede the `catch_all` block, if | ||
any. The `catch`/`catch_all` instructions (within the try construct) are called | ||
the _catching_ instructions. There should be at least `catch` or `catch_all` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the _catching_ instructions. There should be at least `catch` or `catch_all` | |
the _catching_ instructions. There should be at least one `catch` or `catch_all` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd actually drop this requirement. There is no particular reason to disallow (i.e., special-case) the empty handler list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it's always preferable to avoid discontinuities and corner case restrictions unless there's a good reason to have them. AFAIK, there is no other case where we impose such a restriction. For example, sections can be empty a block can be empty, although that's equivalent to a nop, etc. -- there are various cases like this that are seemingly useless but allowed. Not having to worry about discontinuities can ease life for consumers and even more so, for producers. For example, if a producer generates a construct from a list of something, it doesn't need to implement extra logic for the empty list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think try-catch is a little different case then just having a list of of something (e.g. functions) that can be empty. It was designed as a two-parts instruction from the beginning, and while I don't think it's a groundbreaking change, many parts of toolchain have to treat try without catch as a special case, because most parts are written under the assumption there will be two parts.
Also I think this is a kind of big change that has not been discussed so far, so I think it warrants a separate discussion thread..?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still two parts, but one is a list, and the list can have any length, including length 0. :) I don't think this is different from any other similar case, where we have deliberately refrained from making length 0 a special case -- there's not a single occurrence of X^+
in the entire Wasm grammar. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, maybe that doesn't need to be treated as a special case. But anyway, I think we should make this a separate issue post first for more visibility than just changing this here and merging it in.
on top of the stack is popped and then thrown. The `rethrow` instruction traps | ||
if the value on the top of the stack is null. | ||
The `rethrow` instruction can only appear in the body of a catch/catch_all block | ||
but not within the body of an unwind block. It always re-throws the exception |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think saying that rethrow
cannot appear in an unwind
block is too restrictive, consider the following example:
try
...
catch
try
...
unwind
rethrow 1
end
end
The rethrow
appears in the unwind
block, but IIUC this is ok because we never rethrow its exception. Should the rule rather be something like "the rethrow
immediate can only be a catch
or catch_all
block"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't understand why we are making this restriction. IIUC, for N > 0
the following code
try bt
instr1*
unwind
instr2*
rethrow N
instr3*
end
is equivalent to
try bt
instr1*
unwind
instr2*
br 0
instr3*
end
rethrow N-1
For N=0,
try bt
instr1*
unwind
instr2*
rethrow 0
instr3*
end
is equivalent to
try bt
instr1*
unwind
instr2*
end
In both cases, instr3*
is redundant.
So I don't understand what situation is prevented with this restriction. Am I (still) misunderstanding the semantics of unwind
?
(cc. @RossTate)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarification: I do agree with @thibaudmichaud that, if we do add a restriction, then it should be on the rethrow target (i.e., that rethrow cannot target an unwind block).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed with @thibaudmichaud and @ioannad. The restriction is stated the wrong way. It should simply say that the rethrow target must be a catch block, which automatically implies that it has to be properly nested into one.
I also agree that there is no particular reason to disallow rethrow targetting unwind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forbidding rethrow would make sense for finally, because that has nothing to rethrow if the block was entered regularly, but no real point for unwind (unless one assumes we will be adding yet some other form of abort that is neither a trap nor an exception, but that's hardly desirable). But it's conservative to disallow it, so okay to leave it out for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rossberg IIUC also having br
, br_if
, br_table
, or return
among the instructions of an unwind
clause would abort the unwinding process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A rethrow
that "targets" unwind
is problematic because unwind
can fire due to things besides exceptions. But a rethrow
that is simply a throw
of a caught exception should be allowed within unwind
, just like throw
is. (For supporting Common Lisp, it is necessary that exceptions be throwable from within unwind
clauses.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ioannad, but branches do not invoke intermediate unwind clauses, IIUC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I clarified the rethrow-unwind relationship to match the rule we agreed on here: 4d6c635
A try-catch block contains zero or more `catch` blocks and zero or one | ||
`catch_all` block. All `catch` blocks should precede the `catch_all` block, if | ||
any. The `catch`/`catch_all` instructions (within the try construct) are called | ||
the _catching_ instructions. There should be at least `catch` or `catch_all` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd actually drop this requirement. There is no particular reason to disallow (i.e., special-case) the empty handler list.
`catch_all` instructions. | ||
When the specified label within a `delegate` | ||
instruction does not correspond to a `try` instruction, it is a validation | ||
failure. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, this is an unnecessary restriction as well. We could specify it as simply being equivalent to a rethrow inside the target block, which makes it agnostic to what sort of construct it is. That also increases flexibility, because it allows to delegate to an enclosing handler that's not in lexical scope, which is more in line with the dynamically scoped nature of exception handlers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed this at length in some of comments in #143 and #146, and it seems people there, including me, agreed that validation failure is clearer. I also would like to strongly advocate for this, because assuming exception can slip until it meets any enclosing try will make toolchain optimization, such as in Binaryen, very difficult.
@@ -326,20 +381,20 @@ document](https://github.com/WebAssembly/spec/blob/master/document/core/text/ins | |||
The following rules are added to *instructions*: | |||
|
|||
``` | |||
try blocktype instruction* catch instruction* end | | |||
try blocktype instruction* (catch instruction*)+ (catch_all instruction*)+ end | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This grammar would require at least one catch and one catch_all, and allows multiple of the latter. I think you want
try blocktype instruction* (catch instruction*)+ (catch_all instruction*)+ end | | |
try blocktype instruction* (catch instruction*)* (catch_all instruction*)? end | |
This allows a try with no catch blocks, which I think is preferable anyway. If you want to rule that out, you'll need to complicate the grammar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the catch. But I'm not sure why we would need catchless try, given that it is the same as a block
? This is something we haven't discussed so far, so do you mind if we discuss this in somewhere else and add to the explainer if we decide to do that? For now I'll fix this to match the current spec.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Co-authored-by: Andreas Rossberg <rossberg@mpi-sws.org> Co-authored-by: Thomas Lively <7121787+tlively@users.noreply.github.com>
As of commit 275c449 - `rethrow` is as in the first proposal. - Labels do get a new attribute `kind` which is set to `try` or `catch' for labels surrounding instructions which start with `try` or `catch` respectively, and empty otherwise. This is used to validate `delegate` and `rethrow`/`unwind` respectively. - `unwind` can no longer be a target of `rethrow`'s immediate - The `Caught` stack is removed. I also added a file with Wasm code examples from comments (referenced), and what they reduce to according to these semantics. The first example is the only one with a full reduction, and it uses all new instructions, so it's hopefully easy to get an idea of how this works, even for readers without much formal spec involvement.
Thanks for all the comments. I think I addressed most of them. One suggestion was whether we should allow catchless trys, but I think that discussion can continue in a separate issue and can be merged to the explainer later if we decide to make changes. I'll merge this shortly if no one has any more comments. |
This is an attempt to formally describe @aheejin's 3rd proposal, which she presented to the Wasm CG, and which was voted to be the new EH proposal, on September 18, 2020. This is not formal spec that I have developed, but a formal description of this 3rd proposal. This is a reworked form of my [first attempt on this formal spec overview](WebAssembly#87 (comment)) edited with my new understanding of the spec based on the discussion below, and in other issues, and according to @aheejin 's [3rd proposal overview](https://github.com/WebAssembly/exception-handling/blob/f7a4f60d11fb6326fc13f84d3889b11d3873f08a/proposals/Exceptions.md) in PR WebAssembly#137. This is in the form of a document as @tlively [requested](WebAssembly#142 (comment)), to make discussion on specific points easier. I wrote this formal spec overview roughly in the style of the 2nd proposal's [formal spec overview](WebAssembly#87 (comment)) by @rossberg. Particular points of interest: - In this I assume `rethrow` does have an immediate, as it is now described in WebAssembly#137. - The instruction `unwind` now reduces to `catch_all ... rethrow` as specified in Heejin's overview. - Because unwind is much simpler in this MVP, there is no `throw_point` anymore and `caught_m` is much simpler. - The introduction of `caught_m` now also introduces a label around the catch instructions, which label a rethrow instruction can reference. - Added explanation of the peculiar side condition in try's execution rule - just trying to make the rules more compact. I would be happy if anyone could point out things that are wrong or that I misunderstood.
As of commit 275c449 - `rethrow` is as in the first proposal. - Labels do get a new attribute `kind` which is set to `try` or `catch' for labels surrounding instructions which start with `try` or `catch` respectively, and empty otherwise. This is used to validate `delegate` and `rethrow`/`unwind` respectively. - `unwind` can no longer be a target of `rethrow`'s immediate - The `Caught` stack is removed. I also added a file with Wasm code examples from comments (referenced), and what they reduce to according to these semantics. The first example is the only one with a full reduction, and it uses all new instructions, so it's hopefully easy to get an idea of how this works, even for readers without much formal spec involvement.
This is an attempt to formally describe @aheejin's 3rd proposal, which she presented to the Wasm CG, and which was voted to be the new EH proposal, on September 18, 2020. This is not formal spec that I have developed, but a formal description of this 3rd proposal. This is a reworked form of my [first attempt on this formal spec overview](WebAssembly#87 (comment)) edited with my new understanding of the spec based on the discussion below, and in other issues, and according to @aheejin 's [3rd proposal overview](https://github.com/WebAssembly/exception-handling/blob/f7a4f60d11fb6326fc13f84d3889b11d3873f08a/proposals/Exceptions.md) in PR WebAssembly#137. This is in the form of a document as @tlively [requested](WebAssembly#142 (comment)), to make discussion on specific points easier. I wrote this formal spec overview roughly in the style of the 2nd proposal's [formal spec overview](WebAssembly#87 (comment)) by @rossberg. Particular points of interest: - In this I assume `rethrow` does have an immediate, as it is now described in WebAssembly#137. - The instruction `unwind` now reduces to `catch_all ... rethrow` as specified in Heejin's overview. - Because unwind is much simpler in this MVP, there is no `throw_point` anymore and `caught_m` is much simpler. - The introduction of `caught_m` now also introduces a label around the catch instructions, which label a rethrow instruction can reference. - Added explanation of the peculiar side condition in try's execution rule - just trying to make the rules more compact. I would be happy if anyone could point out things that are wrong or that I misunderstood.
As of commit 275c449 - `rethrow` is as in the first proposal. - Labels do get a new attribute `kind` which is set to `try` or `catch' for labels surrounding instructions which start with `try` or `catch` respectively, and empty otherwise. This is used to validate `delegate` and `rethrow`/`unwind` respectively. - `unwind` can no longer be a target of `rethrow`'s immediate - The `Caught` stack is removed. I also added a file with Wasm code examples from comments (referenced), and what they reduce to according to these semantics. The first example is the only one with a full reduction, and it uses all new instructions, so it's hopefully easy to get an idea of how this works, even for readers without much formal spec involvement.
Previously we assumed `rethrow`'s argument was always 0, but it turned out `rethrow` follows the same rule with `br` or `delegate`: WebAssembly/exception-handling#137 WebAssembly/exception-handling#146 (comment) Currently `rethrow`s generated by our backend always rethrow the exception caught by the innermost enclosing catch, so this adds a function to compute that and replaces `rethrow`'s argument with its computed result. This also renames `EHPadStack` in `InstPrinter` to `TryStack`, because in CFGStackify we use `EHPadStack` to mean the range between `catch`~`end`, while in `InstPrinter` we used it to mean the range between `try`~`catch`, so choosing different names would look clearer. Doesn't contain any functional changes in `InstPrinter`. Reviewed By: dschuff Differential Revision: https://reviews.llvm.org/D96595
This updates the explainer text according to the new spec we agreed in
the 09-15-2020 CG meeting. Some of the text is taken from the first
version
of the proposal. I renamed
catch_br
todelegate
based on theperceived consensus in #133.
There are some minor issues that need clarification, some of which is
currently being discussed in the repo:
rethrow
's immmediate (or label) argument. I made it match the statusin the first proposal for now. I believe keeping it makes it more
versatile and lets us handle more languages.
rethrow
anddelegate
's label (=immediate)argument should count non-try block-like structures, such as blocks
and loops. It is currently being discussed for
delegate
.delegate
's opcode should be decided. We exhausted all control-flowopcodes that begin with
0x0
.